package com.zy.explore.db.infrastructure.plugins.multidbsupport.plugin;

import com.zy.explore.db.infrastructure.plugins.util.SqlPrintUtil;
import com.zy.explore.db.infrastructure.plugins.multidbsupport.sqlchange.Sqlchange;
import com.zy.explore.db.infrastructure.plugins.multidbsupport.sqlchange.base.SqlOper;
import com.zy.explore.db.infrastructure.plugins.multidbsupport.sqlchange.function.AbstractFunChangeFactory;
import com.zy.explore.db.infrastructure.plugins.multidbsupport.sqlchange.function.DMFunChangeFactory;
import com.zy.explore.db.infrastructure.plugins.multidbsupport.sqlchange.function.SqlFunctionUtils;
import com.zy.explore.db.infrastructure.plugins.multidbsupport.sqlchange.select.SqlSelectUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.ParameterMapping;
import org.apache.ibatis.mapping.ResultMap;
import org.apache.ibatis.mapping.SqlCommandType;
import org.apache.ibatis.mapping.SqlSource;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.plugin.Intercepts;
import org.apache.ibatis.plugin.Invocation;
import org.apache.ibatis.plugin.Plugin;
import org.apache.ibatis.plugin.Signature;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;

import java.util.List;
import java.util.Map;
import java.util.Properties;


/**
 * 达梦数据库，sql拦截器
 *
 * @author 中国印
 * @date 2022年02月16日 22:53:18
 */
@Intercepts({
        @Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class}),
        @Signature(type = Executor.class, method = "update", args = {MappedStatement.class, Object.class})
})
@Slf4j
public class DmSqlChangeInterceptor implements Interceptor {

    // 这是对应上面的args的序号
    private static final int MAPPED_STATEMENT_INDEX = 0;
    private static final int PARAMETER_INDEX = 1;
    private static final int ROWBOUNDS_INDEX = 2;
    private static final int RESULT_HANDLER_INDEX = 3;


    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        final Object[] queryArgs = invocation.getArgs();
        final MappedStatement mappedStatement = (MappedStatement) queryArgs[MAPPED_STATEMENT_INDEX];
        final Object parameter = queryArgs[PARAMETER_INDEX];
        final BoundSql boundSql = mappedStatement.getBoundSql(parameter);

        // 打印sql和执行时间
        Object returnValue = SqlPrintUtil.printNonQuerySql(invocation);

        //只修改select语句
        if (mappedStatement.getSqlCommandType() != SqlCommandType.SELECT) {
            return returnValue;
        }

        String sql = boundSql.getSql();

//		System.out.println("mappedStatement id："+mappedStatement.getId());
//		
//		if("com.frame.modules.dabis.archives.mapper.ArchivesFileDao.selectIdByStatisticalQuery".equals(mappedStatement.getId())){
//			System.out.println();
//		}

        //函数替换
        List<SqlOper> sqlOperList = SqlFunctionUtils.find(sql, funChangeFactory);

        List<ResultMap> resultMaps = mappedStatement.getResultMaps();
        if (resultMaps != null) {
            for (ResultMap resultMap : resultMaps) {
                //返回结果为map类型
                if (resultMap.getType() != null && Map.class.isAssignableFrom(resultMap.getType())) {
                    //修改select字段：加上双引号强制转换大小写
                    List<SqlOper> sqlOperList3 = SqlSelectUtils.find(sql);
                    sqlOperList.addAll(sqlOperList3);
                    break;
                }
            }
        }

        //sqlOperList.addAll(SqlWhereEmptyUtils.find(sql));
        //不存在需要修改的
        if (sqlOperList.isEmpty()) {
            return returnValue;
        }

        //修改sql
        sql = Sqlchange.change(sql, sqlOperList);

        // 重新new一个查询语句对像
        BoundSql newBoundSql = new BoundSql(mappedStatement.getConfiguration(), sql, boundSql.getParameterMappings(), boundSql.getParameterObject());
        // 把新的查询放到statement里
        MappedStatement newMs = copyFromMappedStatement(mappedStatement, new BoundSqlSqlSource(newBoundSql));
        for (ParameterMapping mapping : boundSql.getParameterMappings()) {
            String prop = mapping.getProperty();
            if (boundSql.hasAdditionalParameter(prop)) {
                newBoundSql.setAdditionalParameter(prop, boundSql.getAdditionalParameter(prop));
            }
        }
        queryArgs[MAPPED_STATEMENT_INDEX] = newMs;
        return returnValue;
    }



    @Override
    public Object plugin(Object target) {
        return Plugin.wrap(target, this);
    }

    @Override
    public void setProperties(Properties properties) {

    }

    private MappedStatement copyFromMappedStatement(MappedStatement ms, SqlSource newSqlSource) {
        MappedStatement.Builder builder = new MappedStatement.Builder(ms.getConfiguration(), ms.getId(), newSqlSource, ms.getSqlCommandType());
        builder.resource(ms.getResource());
        builder.fetchSize(ms.getFetchSize());
        builder.statementType(ms.getStatementType());
        builder.keyGenerator(ms.getKeyGenerator());
        if (ms.getKeyProperties() != null && ms.getKeyProperties().length > 0) {
            builder.keyProperty(ms.getKeyProperties()[0]);
        }
        builder.timeout(ms.getTimeout());
        builder.parameterMap(ms.getParameterMap());
        builder.resultMaps(ms.getResultMaps());
        builder.resultSetType(ms.getResultSetType());
        builder.cache(ms.getCache());
        builder.flushCacheRequired(ms.isFlushCacheRequired());
        builder.useCache(ms.isUseCache());
        return builder.build();
    }

    public static class BoundSqlSqlSource implements SqlSource {
        private BoundSql boundSql;

        public BoundSqlSqlSource(BoundSql boundSql) {
            this.boundSql = boundSql;
        }

        @Override
        public BoundSql getBoundSql(Object parameterObject) {
            return boundSql;
        }
    }

    static final AbstractFunChangeFactory funChangeFactory = new DMFunChangeFactory();

    @SuppressWarnings("unchecked")
    private String changeSql(String sql) {
        List<SqlOper> sqlOperList1 = SqlFunctionUtils.find(sql, funChangeFactory);

        //List<SqlOper> sqlOperList2 = SqlLimitUtils.find(sql);

        List<SqlOper> sqlOperList3 = SqlSelectUtils.find(sql);

        List<SqlOper> mergeList = Sqlchange.mergeList(sqlOperList1, sqlOperList3);

        return Sqlchange.change(sql, mergeList);
    }

    // public static void main(String[] args) {
    //     String sql = "";
    //     List<SqlOper> sqlOperList = SqlFunctionUtils.find(sql, funChangeFactory);
    // }

}
